package com.ar4j.handler;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

import net.sf.cglib.proxy.MethodProxy;

import com.ar4j.ActiveRecordContext;
import com.ar4j.ActiveRecordMetadata;
import com.ar4j.sql.ExtendedMapSqlParameterSource;
import com.ar4j.sql.SqlUtils;

/**
 * A handler for active record find operations
 */
public class CountMethodHandler implements IMethodHandler {
  private String queryPrefix;
  private ReentrantLock prefixCreateLock = new ReentrantLock();
  
  @SuppressWarnings("unchecked")
  @Override
  public Object intercept(ActiveRecordMetadata metadata, ActiveRecordContext context, Object target, Method method, Object[] args, MethodProxy proxy) 
    throws Throwable {
    
    // compose the prefix if its the first time we are running
    if(queryPrefix == null) {
      prefixCreateLock.lock();
      try {
        if(queryPrefix == null) {
          queryPrefix = composeQueryPrefix(metadata.getTableName());
        }
      } finally {
        prefixCreateLock.unlock();
      }
    }
    
    // make sure we have a template first
    if(context.getSimpleTemplate() == null) {
      throw new IllegalStateException("Cannot execute count on an active record that does not have a datasource set");
    }
    
    // build field list and
    Map<String, Object> parameters = (Map<String, Object>) args[0];
    Collection<String> fields = new HashSet<String>();
    Collection<String> collectionFields = new HashSet<String>();
    HandlerUtils.scanParametersForFields(parameters, metadata, fields, collectionFields);
    
    // build parameters and get query
    String sql = composeFullQuery(fields, collectionFields, metadata.getPropertyColumnNameMap());
    ExtendedMapSqlParameterSource source = new ExtendedMapSqlParameterSource(parameters, metadata.getPropertyTypeConvertorMap());
    
    return context.getSimpleTemplate().queryForLong(sql, source);
  }

  private String composeQueryPrefix(String tableName) {
    StringBuilder sql = new StringBuilder();
    sql.append("SELECT COUNT(*) FROM ");
    sql.append(tableName);
    sql.append(" T ");
    
    return sql.toString();
  }
  
  private String composeFullQuery(Collection<String> fields, Collection<String> collectionFields, Map<String, String> fieldToColumnMap) {
    StringBuilder sql = new StringBuilder();
    sql.append(queryPrefix);
    
    if(fields.size() > 0) {
      SqlUtils.appendWhereClause(sql, fields, collectionFields, "T", fieldToColumnMap);
    }
    
    return sql.toString();
  }
  
  @Override
  public String toString() {
    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
  }
}
